WebGL 顶点缓冲对象创建与 attribute 布局
一句话结论:顶点缓冲对象负责把数据搬到显存,attribute 绑定负责告诉 GPU 怎么读这些数据,两者的参数必须与顶点结构严格对齐。
顶点缓冲对象创建与上传
是什么
顶点缓冲对象(VBO)是 GPU 显存中存储顶点数据的缓冲区。创建流程是生成缓冲对象、绑定到目标、上传数据三个动作,绑定状态决定后续哪些操作作用于这个缓冲。
解决什么问题
它解决顶点数据如何被 GPU 高效读取的问题。JavaScript 的普通数组位于 CPU 内存,GPU 无法直接访问,每帧重新传输不仅慢,还会产生同步等待。把数据一次性上传到显存后,绘制时只需绑定引用,不再重复传输。
为什么这样做
显存与主存的带宽差了几个数量级,而顶点数据在一次渲染中要被读取多次(顶点着色器、图元装配都可能访问)。因此设计上把上传与使用分离:上传代价一次性付出,绘制阶段只付绑定成本,整体开销显著降低。
怎么用
// 1. 创建并绑定缓冲对象
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// 2. 上传顶点数据(一次性,静态几何)
const vertices = new Float32Array([
// x, y, z, r, g, b
-0.5, -0.5, 0, 1, 0, 0,
0.5, -0.5, 0, 0, 1, 0,
0.0, 0.5, 0, 0, 0, 1,
]);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 3. 运行期错误检查
if (gl.getError() !== gl.NO_ERROR) console.warn('顶点数据上传异常');
常见坑
- 每帧调用
bufferData上传静态数据 → 原因:把上传当成普通赋值 → 处理:静态几何只上传一次,动态数据改用bufferSubData - 上传前忘记绑定缓冲 → 原因:
bufferData作用于当前绑定的 ARRAY_BUFFER → 处理:先bindBuffer再上传 - 用完不释放 → 原因:页面切换后显存不回收 → 处理:销毁场景时
deleteBuffer(vbo) - 数据类型用普通数组 → 原因:驱动只接受 TypedArray → 处理:统一使用
Float32Array、Uint16Array
attribute 绑定与顶点布局描述
是什么
attribute 绑定与顶点布局描述,是把着色器中的 attribute 变量与缓冲中的字节位置对应起来的过程。核心接口是 getAttribLocation、enableVertexAttribArray 与 vertexAttribPointer,分别负责定位、启用与描述。
解决什么问题
它解决“GPU 怎么知道每个顶点的位置和颜色从哪读”的问题。缓冲只是一段连续字节,必须由布局描述指出每个属性的分量个数、类型、步长与起始偏移,GPU 才能按顶点切分数据。
为什么这样做
交错存放的顶点数据无法靠固定规则解析,因为不同模型的顶点结构完全不同。因此设计上把描述权交给开发者:通过一次调用声明“每 24 字节是一个顶点,位置在前 12 字节,颜色在后 12 字节”,驱动即可按此规则建立读取路径。
怎么用
const STRIDE = 6 * 4; // 每个顶点 6 个 float,共 24 字节
const aPosition = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(
aPosition,
3, // 分量个数:x, y, z
gl.FLOAT, // 分量类型
false, // 是否归一化
STRIDE, // 步长:字节
0 // 偏移:字节
);
const aColor = gl.getAttribLocation(program, 'aColor');
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, STRIDE, 3 * 4);
| 参数 | 含义 | 常见错误 |
|---|---|---|
| size | 每个顶点的分量数 | 写成顶点总数 |
| stride | 相邻顶点字节间隔 | 交错布局时写成 0 |
| offset | 属性起始字节偏移 | 用元素个数代替字节数 |
常见坑
- 偏移按元素个数填写 → 原因:接口要求字节偏移 → 处理:元素个数乘以类型字节数
- 交错布局步长写成 0 → 原因:误以为 0 表示自动计算 → 处理:0 只在紧密连续的单属性缓冲中成立
- 程序切换后继续用旧 location → 原因:location 与 program 绑定 → 处理:每次切换 program 重新获取并绑定
- 忘记启用顶点属性数组 → 原因:默认处于关闭状态 → 处理:绑定前调用
enableVertexAttribArray
要点总结
- 顶点数据必须先上传到缓冲对象,GPU 才能读取
- 静态几何只上传一次,动态数据用
bufferSubData局部更新 - attribute 绑定三件套是定位、启用、描述,缺一不可
vertexAttribPointer的步长与偏移单位是字节,不是元素个数- 交错布局必须显式给出步长,0 只适用于单属性连续数据
